#!/usr/bin/python
# -*- coding: utf8 -*-

from expresion import Expresion, Tipo, Exp, TipoExp, TypeException
from expresion import makeSintaxis

from booleans import Boolean, Verdadero, Falso
from lenguaje import Extension


#######################################
# Declaración del nuevo tipo Maybe(T) #
#######################################

class TipoMaybe(Tipo):
    @classmethod
    def sintaxis(cls):
        return makeSintaxis('Maybe(', TipoExp, ')')

    def __init__(self, toks):
        self.tipo = toks[0]

    def __eq__(self, otro):
        return self.__class__ == otro.__class__ and \
               self.tipo == otro.tipo

    def __str__(self):
        return 'Maybe(%s)' % self.tipo


######################################
# Declaración de nuevas expresiones  #
######################################

class Just(Expresion):
    @classmethod
    def sintaxis(cls):
        return makeSintaxis('just(', Exp, ')')

    def __init__(self, toks):
        self.exp = toks[0]

    def __str__(self):
        return 'just(%s)' % self.exp

    def reducir(self):
        e = self.exp.reducir()
        return Just([e])

    def tipar(self, namespace):
        return TipoMaybe([self.exp.tipar(namespace)])

    def sustituir(self, var, expresion):
        return Just([self.exp.sustituir(var, expresion)])

    
class Nothing(Expresion):
    @classmethod
    def sintaxis(cls):
        return makeSintaxis('nothing(', TipoExp, ')')

    def __init__(self, toks):
        self.tipo = toks[0]

    def __str__(self):
        return 'nothing(%s)' % self.tipo

    def reducir(self):
        return self

    def tipar(self, namespace):
        return TipoMaybe([self.tipo])

    def sustituir(self, var, expresion):
        return self


class IsNothing(Expresion):
    @classmethod
    def sintaxis(cls):
        return makeSintaxis('isNothing(', Exp, ')')

    def __init__(self, toks):
        self.exp = toks[0]

    def __str__(self):
        return 'isNothing(%s)' % self.exp

    def reducir(self):
        e = self.exp.reducir()
        if isinstance(e,Nothing):
            return Verdadero([])
        else:
            return Falso([])


    def tipar(self, namespace):
        st = self.exp.tipar(namespace)
        if isinstance(st, TipoMaybe):
            return Boolean([])
        else:
            raise TypeException(('Tipo del Término: %s \n' + 
                                'El tipo del término debe ser Maybe(T).') % st)
    
    def sustituir(self, var, expresion):
        return IsNothing([self.exp.sustituir(var, expresion)])


class CaseMaybe(Expresion):
    @classmethod
    def sintaxis(cls):
        return makeSintaxis('caseMaybe(', TipoExp, ')', Exp, '; of nothing -> ', Exp, '; just(J) -> ', Exp)

    def __init__(self, toks):
        self.tipo, self.exp, self.resNothing, self.resJust = toks

    def __str__(self):
        return 'caseMaybe(%s) %s; of nothing -> %s; just(J) -> %s' % \
                (self.tipo, self.exp, self.resNothing, self.resJust)

    def reducir(self):
        expR = self.exp.reducir()
        if isinstance(expR, Nothing):
            return self.resNothing.reducir()
        else:
            return self.resJust.sustituir('J', expR.exp).reducir()

    def tipar(self, namespace):
        maybet = self.exp.tipar(namespace)
        trn = self.resNothing.tipar(namespace)
        
        ns_nuevo = namespace.copy()
        ns_nuevo['J'] = self.tipo
        trj = self.resJust.tipar(ns_nuevo)

        if isinstance(maybet, TipoMaybe) and trn == trj:
            return trn
        else:
            raise TypeException(('Tipo del Término: %s \n' +
                                'Tipo del bloque Case Nothing: %s \n' + 
                                'Tipo del bloque Case Just: %s \n' +
                                'El tipo de ambos casos debe ser compatible, y el tipo del término debía ser Maybe(T).') % 
                                (maybet, trn, trj))

    def sustituir(self, var, expresion):
        sMaybe = self.exp.sustituir(var, expresion)
        sresNothing = self.resNothing.sustituir(var, expresion)
        
        if var != 'J':
            sresJust = self.resJust.sustituir(var, expresion)
        else:
            sresJust = self.resJust

        return CaseMaybe([self.tipo, sMaybe, sresNothing, sresJust])


######################################
# Armado de la nueva extensión       #
######################################

extensionMaybe = Extension('Maybe', expresiones=[Just, Nothing, IsNothing, CaseMaybe], tipos=[TipoMaybe])

